#ifndef NR_NET_CLIENT_H
#define NR_NET_CLIENT_H

#include "nr_data.h"
#include "nr_device_type.h"
#include "nr_net_socket.h"
#include "nr_net_command.h"

class nr_net_client_data_delegate
{
public:
    virtual nr_error_t on_receive_data(nr_data *data) = 0;
    virtual nr_error_t on_receive_event(nr_event *evt) = 0;
};

class nr_net_client_receive_event_delegate
{
public:
    virtual nr_error_t on_receive_event(nr_event *evt) = 0;
};

class nr_net_reply_delegate
{
public:
    virtual void on_error(nr_error_t error) = 0;
    virtual void on_reply(nr_object &reply) = 0;
};

class nr_net_request_data
{
public:
    nr_net_request_data(nr_net_command_type cmd_type, 
        nr_object *request, nr_object *reply, 
        nr_net_reply_delegate *delegate)
        : m_cmd(cmd_type)
        , m_request(request)
        , m_reply(reply)
        , m_delegate(delegate)
    {
        m_seq_no = generate_seq_no();
    }
    
    nr_net_request_data(nr_net_command_type cmd_type,
        nr_net_reply_delegate *delegate)
        : m_cmd(cmd_type)
        , m_delegate(delegate)
    {
        m_seq_no = generate_seq_no();
        m_request = nr_net_helper::create_command_by_type(cmd_type, true);
        m_reply = nr_net_helper::create_command_by_type(cmd_type, false);
    }
    
    ~nr_net_request_data()
    {
        delete m_request;
        delete m_reply;
    }
    
    static qint32 generate_seq_no();
    
    qint32& seq_no() { return m_seq_no; }
    nr_net_command_type command() { return m_cmd; }
    nr_object *request() { return m_request; }
    nr_object *reply() { return m_reply; }
    nr_net_reply_delegate* delegate() { return m_delegate; } 

private:
    qint32 m_seq_no;
    nr_net_command_type m_cmd;
    nr_object *m_request;
    nr_object *m_reply;
    nr_net_reply_delegate *m_delegate;
    static qint32 g_seq_no;
};

class nr_net_client : public QObject
{
    Q_OBJECT
public:
    nr_net_client(QObject *parent);
    ~nr_net_client();
        
    QString& host() { return m_host; }
    
    quint16& port() { return m_port; }

    qint32& session_id() { return m_seesion_id; }

    nr_error_t login(const QString& username, const QString& password, nr_net_reply_delegate *delegate);
    nr_error_t logout(nr_net_reply_delegate *delegate);

    nr_error_t get_device_items(nr_net_reply_delegate *delegate);

    nr_error_t get_device_types(nr_device_types& device_types, nr_net_reply_delegate *delegate); 

    void set_data_delegate(nr_net_client_data_delegate* delegate) { m_data_delegate = delegate; }

    nr_error_t start_data(const nr_device_id_t& device_id, nr_net_reply_delegate *delegate);
    nr_error_t start_data(const QList<nr_device_id_t>& device_id_list, nr_net_reply_delegate *delegate);
    nr_error_t start_all_data(nr_net_reply_delegate *delegate);

    nr_error_t stop_data(const nr_device_id_t& device_id, nr_net_reply_delegate *delegate);
    nr_error_t stop_data(const QList<nr_device_id_t>& device_id_list, nr_net_reply_delegate *delegate);
    nr_error_t stop_all_data(nr_net_reply_delegate *delegate);

    nr_error_t query_data(const nr_device_id_t& device_id, QDateTime &begin_time, 
        QDateTime &end_time, nr_net_reply_delegate *delegate);

    nr_error_t query_event(const nr_device_id_t& device_id, QDateTime &begin_time, 
        QDateTime &end_time, QList<nr_event*>& data_list);

    virtual nr_error_t set_property(const nr_device_id_t& device_id, nr_id_t prop_id, const QVariant &in_prop) { return NR_ERROR_NOT_IMPLEMENTED; }

    virtual nr_error_t get_property(const nr_device_id_t& device_id, nr_id_t prop_id, QVariant &out_prop) { return NR_ERROR_NOT_IMPLEMENTED; }

    virtual nr_error_t perform_action(const nr_device_id_t& device_id, nr_id_t action_id, const QVariantHash &in_params, QVariantHash &out_params) { return NR_ERROR_NOT_IMPLEMENTED; }

private:
    bool is_data_command(nr_net_command_type_t cmd_type);
    nr_error_t process_data(nr_net_command_type_t cmd_type, QByteArray &byte_array);

    void pre_process_reply_internal(nr_net_command_header &header, nr_net_request_data &request_data);
    void pre_process_error_internal(nr_net_request_data &request_data, nr_error_t error);

    nr_error_t send_request_data(nr_net_request_data &request_data);
    void push_request_data(nr_net_request_data *request_data);
    nr_net_request_data* find_request_data_by_seq_no(qint32 seq_no);
    void remove_request_data_by_seq_no(qint32 seq_no);

    nr_error_t check_header(nr_net_command_header &header) { return NR_ERROR_NONE; }
    
    nr_net_socket *m_socket;
    bool m_is_login;
    QString m_host;
    quint16 m_port;
    qint32 m_seesion_id;

    typedef QHash<qint32, nr_net_request_data*> nr_request_data_hash;
    nr_request_data_hash m_request_queue;
    nr_net_client_data_delegate *m_data_delegate;

    private slots:
        void on_net_connected(nr_net_socket*);
        void on_net_data_connected(nr_net_socket*);
        void on_net_error(nr_net_socket*);
        void on_net_receive_data(nr_net_socket*);
        void on_net_about_to_close(nr_net_socket*);
        void on_net_timeout();
};

#endif // NR_NET_CLIENT_H
